#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#include "string.h"
// 校验和
uint16_t FPM383_CheckSum(uint8_t *buff, uint16_t len)
{
    uint16_t sum = 0;
    for (uint16_t i = 6; i < len - 2; i++)
    {
        sum += buff[i];
    }

    buff[len - 2] = sum >> 8;
    buff[len - 1] = sum;
    return sum;
}
// 发送
void FPM383_Send(uint8_t *buff, uint16_t len)
{
    int num = uart_write_bytes(UART_NUM_1, buff, len);
    if (num < 0)
    {
        ESP_LOGE("", "uart_write_bytes failed, num: %d\n", num);
    }
}
static uint8_t recv_buff[256];
// 接收
void FPM383_Recv(uint16_t len, uint32_t timeout)
{
    memset(recv_buff, 0, sizeof(recv_buff));
    int num = uart_read_bytes(1, recv_buff, len, timeout);
    uart_flush(1);
    if (num < 0)
    {
        ESP_LOGE("", "uart_read_bytes failed, num: %d\n", num);
    }
}
TaskHandle_t fingerScanTaskHandle;
void FPM383_ISR(void)
{
    xTaskNotifyFromISR(fingerScanTaskHandle, (uint32_t)1, eSetValueWithoutOverwrite, NULL);
}
void FPM383_Sleep(void)
{
    uint8_t buff[12] = {0xef, 0x01, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x03, 0x33, '\0', '\0'};
    FPM383_CheckSum(buff, 12);
    do
    {
        FPM383_Send(buff, 12);
        FPM383_Recv(12, 2000);
    } while (recv_buff[9] != 0x00);
    gpio_intr_enable(GPIO_NUM_10); // Enable interrupt
}
void FPM383_Init(void)
{
    uart_driver_install(1, 1024, 0, 0, NULL, 0);
    uart_config_t uart_conf = {
        .baud_rate = 57600,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_DEFAULT,
        .rx_flow_ctrl_thresh = 0};
    uart_param_config(UART_NUM_1, &uart_conf);
    uart_set_pin(UART_NUM_1, GPIO_NUM_21, GPIO_NUM_20, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
    gpio_config_t gpio_conf = {
        .intr_type = GPIO_INTR_POSEDGE,
        .mode = GPIO_MODE_INPUT,
        .pull_up_en = 0,
        .pull_down_en = 1,
        .pin_bit_mask = (1ULL << GPIO_NUM_10)};
    gpio_config(&gpio_conf);
    gpio_isr_handler_add(GPIO_NUM_10, FPM383_ISR, NULL);
    gpio_intr_disable(GPIO_NUM_10); // Disable interrupt
    FPM383_Sleep();
}
// 获取最小值
int16_t FPM383_GetMinId(void)
{
    uint8_t buff[13] = {0xef, 0x01, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x04, 0x1f, 0x00, '\0', '\0'};
    FPM383_CheckSum(buff, 13);
    FPM383_Send(buff, 13);
    FPM383_Recv(44, portMAX_DELAY);
    for (size_t i = 10; i < 42; i++)
    {
        uint8_t data = recv_buff[i];

        for (size_t j = 0; j < 8; j++)
        {
            if ((data & 0x01) == 0)
            {
                return j + (i - 10) * 8;
            }
            data >>= 1;
        }
    }
    return -1;
}
// 取消
void FPM383_CancelAuto(void)
{
    uint8_t buff[12] = {0xef, 0x01, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x03, 0x30, '\0', '\0'};
    FPM383_CheckSum(buff, 12);
    do
    {
        FPM383_Send(buff, 12);
        FPM383_Recv(12, 2000);
    } while (recv_buff[9] != 0x00);
}
// 指纹录入
void FPM383_AutoEnroll(void)
{
    vTaskDelay(1500);
    int16_t id = FPM383_GetMinId();
    ESP_LOGE("", "id = %d", id);
    FPM383_CancelAuto();
    FPM383_CancelAuto();
    FPM383_CancelAuto();
    FPM383_CancelAuto();
    uint8_t buff[17] = {0xef, 0x01, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x08, 0x31, id >> 8, id & 0xff, 0x02, '\0', '\0', '\0', '\0'};
    buff[13] = 0x3b >> 8;
    buff[14] = 0x3b & 0xff;
    FPM383_CheckSum(buff, 17);
    FPM383_Send(buff, 17);
    while (1)
    {
        FPM383_Recv(14, 5000);
        uint8_t packetId = recv_buff[6];
        uint8_t code = recv_buff[9];
        uint8_t arg1 = recv_buff[10];
        uint8_t arg2 = recv_buff[11];
        if (packetId == 0)
            continue;
        if (code != 0x00)
        {
            ESP_LOGE("", "指纹录入失败, 确认码: 0x%02x", code);
            break;
        }
        else if (arg1 == 0x06 && arg2 == 0xf2)
        {
            ESP_LOGE("", "指纹录入成功");
            break;
        }
    }
    FPM383_CancelAuto();
    FPM383_CancelAuto();
    FPM383_CancelAuto();
    FPM383_CancelAuto();
    FPM383_Sleep();
}
// 指纹验证
void FPM383_AutoVerify(void)
{
    FPM383_CancelAuto();
    FPM383_CancelAuto();
    FPM383_CancelAuto();
    FPM383_CancelAuto();
    uint8_t buff[17] = {0xef, 0x01, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x08, 0x32, 0x03, 0xff, 0xff, 0x00, 0x03, '\0', '\0'};
    FPM383_CheckSum(buff, 17);
    FPM383_Send(buff, 17);
    while (1)
    {
        FPM383_Recv(17, portMAX_DELAY);
        if (recv_buff[9] != 0x00)
        {
            ESP_LOGE("", "指纹验证失败");
            break;
        }
        else if (recv_buff[10] == 0x05)
        {
            ESP_LOGE("", "指纹验证成功");
            vTaskDelay(1500);
            break;
        }
    }
    FPM383_CancelAuto();
    FPM383_CancelAuto();
    FPM383_CancelAuto();
    FPM383_CancelAuto();
    FPM383_Sleep();
}
// 清空指纹库
void FPM383_ClearAll(void)
{
    uint8_t emptyBuffer[12] = {0xEF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x03, 0x0D, 0x00, 0x11};
    FPM383_Send(emptyBuffer, 12);
    FPM383_Recv(12, portMAX_DELAY);
}
// 背光提示
void FPM383_LedControl(uint8_t fun, uint8_t startColor, uint8_t endColor, uint8_t cycle)
{
    uint8_t cmd[16] = {0xEF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x07, 0x3C, '\0', '\0', '\0', '\0', '\0', '\0'};
    cmd[10] = fun;
    cmd[11] = startColor;
    cmd[12] = endColor;
    cmd[13] = cycle;
    FPM383_CheckSum(cmd, 16);
    FPM383_Send(cmd, 16);
    FPM383_Recv(12, portMAX_DELAY);
}
#define LED_RED_FLICKER FPM383_LedControl(2, 0x04, 0x04, 0);
#define LED_GREEN_FLICKER FPM383_LedControl(2, 0x02, 0x02, 0);
#define LED_BLUE_FLICKER FPM383_LedControl(2, 0x01, 0x01, 0);
#define LED_RED_ON FPM383_LedControl(3, 0x04, 0x04, 0);
#define LED_GREEN_ON FPM383_LedControl(3, 0x02, 0x02, 0);
#define LED_BLUE_ON FPM383_LedControl(3, 0x01, 0x01, 0);
#define LED_COLOR_OFF FPM383_LedControl(4, 0x07, 0x07, 0);
void app_main(void)
{
    gpio_install_isr_service(0);
    FPM383_Init();
    fingerScanTaskHandle = xTaskGetCurrentTaskHandle();
    while (1)
    {
        uint32_t action = 0;
        ESP_LOGE("", "添加指纹");
        LED_GREEN_FLICKER;
        FPM383_AutoEnroll();
        LED_COLOR_OFF;
        xTaskNotifyWait(0xffffffff, 0xffffffff, &action, portMAX_DELAY);
        ESP_LOGE("", "验证");
        LED_BLUE_ON;
        FPM383_AutoVerify();
        LED_COLOR_OFF;
    }
}